﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using dbPoll_Application.ViewModels;
using dbPoll_Application.Models;
using System.Web.Routing;
using dbPoll_Application.Helpers;
using NHibernate.Linq;

namespace dbPoll_Application.Controllers
{
    public class RespondController : Controller
    {
        [Authorize]
        public ActionResult Index()
        {
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var participants = ParticipantRepository.GetParticipantsByLinkedUser(user);
            var polls = new List<Poll>();
            foreach (var participant in participants)
            {
                polls.AddRange(PollRepository.GetPollContainingParticipant(participant));
            }
            var viewModel = new PollViewModel(polls);
            return View(viewModel);    
        }

        [Authorize]
        public ActionResult ResponseQuestions(int pollID)
        {
            var poll = PollRepository.GetPollByID(pollID);
            var viewModel = new QuestionViewModel(pollID, poll.name, poll.questions);
            return View(viewModel);
        }

        [Authorize]
        public JsonResult GetMessages(int pollID)
        {
            User user = UserRepository.GetUserByUsername(User.Identity.Name);
            var messages = MessageRepository.GetLatestMessages(pollID, user);
            return Json(messages);
        }

        private T Question<T>(int pollID, int questionNumber) where T : Question 
        {
            var poll = PollRepository.GetPollByID(pollID);
            var questions = poll.questions.Where(m => m.deletionTime == null);
            var question = questions.ElementAt(questionNumber - 1) as T;
            if (poll.isTestPoll)
            {
                ViewData["pollID"] = poll.linkedPoll.pollID;
                ViewData["pollName"] = "Test Poll";
                ViewData["questionCount"] = poll.linkedPoll.questions.Count<Question>();
            }
            else
            {
                ViewData["pollID"] = pollID;
                ViewData["pollName"] = poll.name;
                ViewData["questionCount"] = questions.Count<Question>();
            }

            return question;
        }

        [Authorize]
        public ActionResult CustomQuestion(int pollID, int questionNumber)
        {
            return View(Question<CustomQuestion>(pollID, questionNumber));
        }

        [Authorize]
        public ActionResult RankingQuestion(int pollID, int questionNumber)
        {
            return View(Question<RankingQuestion>(pollID, questionNumber));
        }

        [Authorize]
        public ActionResult DemographicQuestion(int pollID, int questionNumber)
        {
            return View(Question<DemographicQuestion>(pollID, questionNumber));
        }

        [Authorize]
        public ActionResult NumericQuestion(int pollID, int questionNumber)
        {
            return View(Question<NumericQuestion>(pollID, questionNumber));
        }

        [Authorize]
        public ActionResult AlphanumericQuestion(int pollID, int questionNumber)
        {
            return View(Question<AlphanumericQuestion>(pollID, questionNumber));
        }

        [Authorize]
        public int SaveCustomResponse(int questionID, int optionID)
        {
            var question = QuestionRepository.GetQuestionByID<CustomQuestion>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            var option = question.options.FirstOrDefault<QuestionOption>(m => m.optionID == optionID);
            var response = new OptionResponse(participant);
            option.responses.Add(response);
            question.responded.Add(participant);
            QuestionRepository.UpdateQuestion<CustomQuestion>(question);

            return response.responseID;
        }

        [Authorize]
        public int SaveRankingResponse(int questionID, int optionID, int rank)
        {
            var question = QuestionRepository.GetQuestionByID<RankingQuestion>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            var option = question.options.FirstOrDefault<QuestionOption>(m => m.optionID == optionID);
            var response = new OptionResponse(participant, rank);
            option.responses.Add(response);
            if (!question.responded.Any(p=>p.Equals(participant))) question.responded.Add(participant);
            QuestionRepository.UpdateQuestion<RankingQuestion>(question);

            return response.responseID;
        }

        [Authorize]
        public int SaveDemographicResponse(int questionID, int optionID)
        {
            var question = QuestionRepository.GetQuestionByID<DemographicQuestion>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            var option = question.options.FirstOrDefault<QuestionOption>(m => m.optionID == optionID);
            var response = new OptionResponse(participant);
            option.responses.Add(response);
            question.responded.Add(participant);
            ParticipantFieldValue fieldValue = null;
            fieldValue = question.linkedField.fieldValues.Where(m => m.participant == response.participant).FirstOrDefault<ParticipantFieldValue>();
            if (fieldValue == null)
            {
                fieldValue = new ParticipantFieldValue(response.participant, option.answer);
                question.linkedField.fieldValues.Add(fieldValue);
            }
            else if (!fieldValue.value.Equals(option.answer))
            {
                fieldValue.value = option.answer;
            }
            QuestionRepository.UpdateQuestion<DemographicQuestion>(question);

            return response.responseID;
        }

        private int SaveShortResponse<T>(int questionID, String answer) where T : ShortResponseQuestion
        {
            var question = QuestionRepository.GetQuestionByID<T>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            var response = new ShortResponse(participant, answer);
            question.responses.Add(response);
            question.responded.Add(participant);
            QuestionRepository.UpdateQuestion<T>(question);

            return response.responseID;
        }

        [Authorize]
        public int SaveNumericResponse(int questionID, String answer)
        {
            return SaveShortResponse<NumericQuestion>(questionID, answer);
        }

        [Authorize]
        public int SaveAlphanumericResponse(int questionID, String answer)
        {
            return SaveShortResponse<AlphanumericQuestion>(questionID, answer);
        }

        private void DeleteOptionResponse<T>(int questionID) where T : MultipleChoiceQuestion
        {
            var question = QuestionRepository.GetQuestionByID<T>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            IList<QuestionOption> option = question.options.Where(o=>o.responses.Any(p=>p.participant.Equals(participant))).ToList();
            foreach (QuestionOption op in option)
            {
                var response = op.responses.Where(m => m.participant.Equals(participant)).ToList() ;
                foreach (var rer in response)
                {
                    rer.participant = null;
                    op.responses.Remove(rer);
                }
            }
            QuestionRepository.UpdateQuestion<T>(question);
        }

        [Authorize]
        public void DeleteCustomResponse(int questionID)
        {
            var question = QuestionRepository.GetQuestionByID<CustomQuestion>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            if (question.options.Any(o => o.responses.Any(p => p.participant.Equals(participant))))
                DeleteOptionResponse<CustomQuestion>(questionID);
        }

        [Authorize]
        public void DeleteRankingResponse(int questionID)
        {
            var question = QuestionRepository.GetQuestionByID<RankingQuestion>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            if (question.options.Any(o => o.responses.Any(p => p.participant.Equals(participant))))
                DeleteOptionResponse<RankingQuestion>(questionID);
        }

        [Authorize]
        public void DeleteDemographicResponse(int questionID, int optionID)
        {
            var question = QuestionRepository.GetQuestionByID<DemographicQuestion>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            if (question.options.Any(o => o.responses.Any(p => p.participant.Equals(participant))))
            {
                var option = question.options.FirstOrDefault<QuestionOption>(m => m.optionID == optionID);
                var response = option.responses.FirstOrDefault<OptionResponse>(m => m.participant.Equals(participant));
                response.participant = null;
                option.responses.Remove(response);
                var fieldValue = question.linkedField.fieldValues.FirstOrDefault<ParticipantFieldValue>(m => m.participant.Equals(participant));
                fieldValue.groupd = null;
                fieldValue.participant = null;
                question.linkedField.fieldValues.Remove(fieldValue);
                QuestionRepository.UpdateQuestion<DemographicQuestion>(question);
            }
        }

        private void DeleteShortResponse<T>(int questionID) where T : ShortResponseQuestion
        {
            var question = QuestionRepository.GetQuestionByID<T>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            var response = question.responses.FirstOrDefault<ShortResponse>(m => m.participant.Equals(participant));
            response.participant = null;
            question.responses.Remove(response);
            QuestionRepository.UpdateQuestion<T>(question);
        }

        [Authorize]
        public void DeleteNumericResponse(int questionID)
        {
            var question = QuestionRepository.GetQuestionByID<NumericQuestion>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            if (question.responses.Any(p => p.participant.Equals(participant)))
                DeleteShortResponse<NumericQuestion>(questionID);
        }

        [Authorize]
        public void DeleteAlphanumericResponse(int questionID)
        {
            var question = QuestionRepository.GetQuestionByID<AlphanumericQuestion>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            if (question.responses.Any(p => p.participant.Equals(participant)))
                DeleteShortResponse<AlphanumericQuestion>(questionID);
        }

        [Authorize]
        public int SaveFeedback(int questionID, String comment)
        {
            var question = QuestionRepository.GetQuestionByID<Question>(questionID);
            var user = UserRepository.GetUserByUsername(User.Identity.Name);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByLinkedUserAndPoll(user, poll);
            var feedback = new Feedback(comment, participant);
            question.feedback.Add(feedback);
            QuestionRepository.UpdateQuestion<Question>(question);

            return feedback.feedbackID;
        }

        [Authorize(Roles = "Poll Master")]
        public Boolean SaveKeypadOptionResponse(String deviceID, String response, int rank, int questionID)
        {
            var question = QuestionRepository.GetQuestionByID<MultipleChoiceQuestion>(questionID);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByDeviceIDAndPoll(deviceID, poll);
            var optionNumber = Convert.ToInt32(response);
            if (question.options.Count >= optionNumber && optionNumber > 0)
            {
                var option = question.options[optionNumber - 1];
                question.responded.Add(participant);
                option.responses.Add(new OptionResponse(participant, rank));

                return QuestionRepository.UpdateQuestion<MultipleChoiceQuestion>(question);
            }
            return false;
        }

        [Authorize(Roles = "Poll Master")]
        public Boolean SaveKeypadNumericResponse(String deviceID, String response, int questionID)
        {
            var question = QuestionRepository.GetQuestionByID<NumericQuestion>(questionID);
            var poll = PollRepository.GetPollContainingQuestion(question);
            var participant = ParticipantRepository.GetParticipantByDeviceIDAndPoll(deviceID, poll);
            var shortResponse = ResponseRepository.GetShortResponseByDeviceID(question.responses, deviceID);
            if (shortResponse != null)
            {
                shortResponse.response = response;
            }
            else
            {
                question.responded.Add(participant);
                question.responses.Add(new ShortResponse(participant, response));
            }
            return QuestionRepository.UpdateQuestion<NumericQuestion>(question);
        }

        [Authorize(Roles = "Poll Master")]
        public Boolean TrimExcessResponses(int questionID)
        {
            var question = QuestionRepository.GetQuestionByID<MultipleChoiceQuestion>(questionID);
            var responses = new Dictionary<Participant, List<OptionResponse>>();
            var toBeTrimmed = new List<OptionResponse>();
            foreach (var participant in question.responded)
            {
                responses[participant] = new List<OptionResponse>();
            }
            foreach (var option in question.options)
            {
                foreach (var response in option.responses)
                {
                    responses[response.participant].Add(response);
                }
            }
            foreach (var kvp in responses)
            {
                var excessAmount = kvp.Value.Count() - question.allowedAnswers;
                var sortedResponses = kvp.Value.OrderBy(m => m.rank);
                if (excessAmount > 0)
                {
                    toBeTrimmed.AddRange(sortedResponses.Take(excessAmount));
                }
                if (excessAmount < 0)
                {
                    excessAmount = 0;
                }
                for (int i = excessAmount, j = 1; i < sortedResponses.Count(); i++, j++)
                {
                    sortedResponses.ElementAt(i).rank = j;
                }
            }
            responses.Clear();
            foreach (var option in question.options)
            {
                foreach (var response in toBeTrimmed)
                {
                    response.participant = null;
                    option.responses.Remove(response);
                }
            }
            toBeTrimmed.Clear();
            return QuestionRepository.UpdateQuestion<MultipleChoiceQuestion>(question);
        }

        [Authorize(Roles = "Poll Master")]
        public Boolean DeleteKeypadOptionResponses(String deviceID, int questionID)
        {
            var question = QuestionRepository.GetQuestionByID<MultipleChoiceQuestion>(questionID);
            foreach (var option in question.options)
            {
                var response = ResponseRepository.GetOptionResponseByDeviceID(option.responses, deviceID);
                if (response != null)
                {
                    response.participant = null;
                    option.responses.Remove(response);
                }
            }
            return QuestionRepository.UpdateQuestion<MultipleChoiceQuestion>(question);
        }

        [Authorize(Roles = "Poll Master")]
        public Boolean DeleteKeypadNumericResponse(String deviceID, int questionID)
        {
            var question = QuestionRepository.GetQuestionByID<NumericQuestion>(questionID);
            var response = ResponseRepository.GetShortResponseByDeviceID(question.responses, deviceID);
            if (response!=null)
            {
                response.participant=null;
                question.responses.Remove(response);
                return QuestionRepository.UpdateQuestion<NumericQuestion>(question);
            }
            return true;
        }

        [Authorize(Roles = "Poll Master")]
        public void AddGridResponse(String deviceID, int pollID)
        {
            var poll = PollRepository.GetPollByID(pollID);
            poll.gridResponses.Add(new Keypad(deviceID));
            PollRepository.UpdatePoll(poll);
        }

        [Authorize(Roles = "Poll Master")]
        public JsonResult GetGridResponses(int pollID)
        {
            return Json(ResponseRepository.GetNextGridResponses(pollID));
        }
    }
}
